/*
Copyright 2012 Aphid Mobile

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
	 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

 */

package com.aphidmobile.flip;

import javax.microedition.khronos.opengles.GL10;

import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;

import com.aphidmobile.flip.FlipViewController.FlipViewOnPageChange;
import com.aphidmobile.utils.TextureUtils;
import com.sylar.happyreader.tools.Utils;

public class FlipCards {
	private static final float ACCELERATION = 0.618f;
	private static final float TIP_SPEED = 1f;
	private static final float MOVEMENT_RATE = 1.5f;
	private static final int MAX_TIP_ANGLE = 60;

	private static final int STATE_INIT = 0;
	private static final int STATE_TOUCH = 1;
	private static final int STATE_AUTO_ROTATE = 2;

	private ViewDualCards frontCards;
	private ViewDualCards backCards;

	private float angle = 0f;
	private boolean forward = true;
	private int animatedFrame = 0;
	private int state = STATE_INIT;
	private boolean onAutoNext = false;

	private float lastY = -1;
	private FlipViewOnPageChange mPageChangelistener;

	@SuppressWarnings("unused")
	private VelocityTracker velocityTracker;
	private FlipViewController controller;

	private int activeIndex = -1;

	private boolean visible = false;

	public FlipCards(FlipViewController controller) {
		this.controller = controller;

		frontCards = new ViewDualCards();
		backCards = new ViewDualCards();

		resetAxises();
	}

	public ViewDualCards getFrontCards() {
		return frontCards;
	}

	public ViewDualCards getBackCards() {
		return backCards;
	}

	public boolean isVisible() {
		return visible;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}

	public void reloadTexture(int frontIndex, View frontView, int backIndex, View backView) {
		synchronized (this) {
			if (frontView != null) {
				if (backCards.getView() == frontView) {
					frontCards.setView(-1, null);
					swapCards();
				}
			}

			if (backView != null) {
				if (frontCards.getView() == backView) {
					backCards.setView(-1, null);
					swapCards();
				}
			}

			boolean frontChanged = frontCards.setView(frontIndex, frontView);
			boolean backChanged = backCards.setView(backIndex, backView);

			if (frontIndex == activeIndex) {
				if (angle >= 180)
					angle -= 180;
				else if (angle < 0)
					angle += 180;
			} else if (backIndex == activeIndex) {
				if (angle < 0)
					angle += 180;
			}
			// AphidLog.i("View changed: front (%d, %s), back (%d, %s), angle %s, activeIndex %d",
			// frontIndex, frontView, backIndex, backView, angle, activeIndex);
		}
	}

	public void rotateBy(float delta) {
		angle += delta;

		if (backCards.getIndex() == -1) {
			if (angle >= MAX_TIP_ANGLE)
				angle = MAX_TIP_ANGLE;
		}

		if (angle > 180)
			angle = 180;
		else if (angle < 0)
			angle = 0;
	}

	public void setState(int state) {
		if (this.state != state) {
			this.state = state;
			animatedFrame = 0;
		}
	}

	public synchronized void draw(FlipRenderer renderer, GL10 gl) {
		applyTexture(renderer, gl);

		if (!TextureUtils.isValidTexture(frontCards.getTexture()) && !TextureUtils.isValidTexture(backCards.getTexture()))
			return;

		if (!visible)
			return;

		switch (state) {
		case STATE_INIT: {

		}
			break;
		case STATE_TOUCH:
			break;
		case STATE_AUTO_ROTATE: {
			animatedFrame++;
			rotateBy((forward ? ACCELERATION : -ACCELERATION) * animatedFrame);

			if (angle >= 180 || angle <= 0) {
				setState(STATE_INIT);
				if (angle >= 180) { // flip to next page
					if (backCards.getIndex() != -1) {
						activeIndex = backCards.getIndex();
						controller.postFlippedToView(activeIndex);
					} else
						angle = 180;
				}
				controller.postHideFlipAnimation();
			} else
				controller.getSurfaceView().requestRender();
			}
			break;
		}
		
		
		if (angle < 90) { // render front view over back view
			
			Utils.LogD("backCards on draw angle < 90 : " + angle);
			
			frontCards.getTopCard().setAngle(0);
			frontCards.getTopCard().draw(gl);

			backCards.getBottomCard().setAngle(0);
			backCards.getBottomCard().draw(gl);

			frontCards.getBottomCard().setAngle(angle);
			frontCards.getBottomCard().draw(gl);
			
		} else { // render back view first
			Utils.LogD("backCards on draw angle >= 90 : " + angle);
			
			frontCards.getTopCard().setAngle(0);
			frontCards.getTopCard().draw(gl);

			backCards.getTopCard().setAngle(180 - angle);
			backCards.getTopCard().draw(gl);

			backCards.getBottomCard().setAngle(0);
			backCards.getBottomCard().draw(gl);
		}
	}

	public void invalidateTexture() {
		frontCards.abandonTexture();
		backCards.abandonTexture();
	}

	public synchronized boolean handleTouchEvent(MotionEvent event, boolean isOnTouchEvent, FlipViewOnPageChange listener) {
		float delta;
		mPageChangelistener = listener;
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastY = event.getY();
			return isOnTouchEvent;
		case MotionEvent.ACTION_MOVE:
			delta = lastY - event.getY();
			if (Math.abs(delta) > controller.getTouchSlop())
				setState(STATE_TOUCH);
			if (state == STATE_TOUCH) {
				controller.showFlipAnimation();
				final float angleDelta = 180 * delta / controller.getContentHeight() * MOVEMENT_RATE;
				angle += angleDelta;
				
				Utils.LogD("backCards on touch angleDelta : " + angleDelta);
				Utils.LogD("backCards on touch angle : " + angle);
				Utils.LogD("backCards.getIndex() : " + backCards.getIndex());
				Utils.LogD("frontCards.getIndex() : " + frontCards.getIndex());
				
				if (backCards.getIndex() == -1) {
					if (angle >= MAX_TIP_ANGLE)
						angle = MAX_TIP_ANGLE;
				} else if (backCards.getIndex() == 0) {
					if (angle <= 180 - MAX_TIP_ANGLE)
						angle = 180 - MAX_TIP_ANGLE;
				}
				if (angle < 0) {
					if (frontCards.getIndex() > 0) {
						activeIndex = frontCards.getIndex() - 1;
						controller.flippedToView(activeIndex);
					} else {
						swapCards();
						frontCards.setView(-1, null);
						if (-angle >= MAX_TIP_ANGLE)
							angle = -MAX_TIP_ANGLE;
						angle += 180;
					}
				}
				
				lastY = event.getY();
				controller.getSurfaceView().requestRender();
				return true;
			}

			return isOnTouchEvent;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_CANCEL:
			if (state == STATE_TOUCH) {
				if (listener != null)
					listener.onPageChange(frontCards.getIndex(), backCards.getIndex());
				delta = lastY - event.getY();
				rotateBy(180 * delta / controller.getContentHeight() * MOVEMENT_RATE);
				forward = angle >= 90;
				setState(STATE_AUTO_ROTATE);
				controller.getSurfaceView().requestRender();
			}else{
				if(!onAutoNext){
					if(lastY > controller.getContentHeight() /2 ){
						autoToNextPage();
					}else{
						autoToBackPage();
					}
					
				}
				
			}
			
			return isOnTouchEvent;
		}

		return false;
	}

	private synchronized void autoToBackPage(){
		onAutoNext = true;
//		Message msg1 = mHandler.obtainMessage();
//		msg1.what = MSG_TYPE_AUTO_BACK_PAGE;
//		msg1.obj = -7;
//		mHandler.sendMessage(msg1);
//		Message msg2 = mHandler.obtainMessage();
//		msg2.what = MSG_TYPE_AUTO_BACK_PAGE;
//		msg2.obj = -7;
//		mHandler.sendMessage(msg2);
		
		new Thread(){
			public void run() {
				for(int i= 180 ;i >= 0;i-=2){
					Message msg = mHandler.obtainMessage();
					msg.what = MSG_TYPE_AUTO_BACK_PAGE;
					msg.obj = i;
					mHandler.sendMessage(msg);
					try {
						Thread.sleep(5);
					} catch (Exception e) {
					}
				}
				
			};
		}.start();
	}
	
	private synchronized void autoToNextPage(){
		onAutoNext = true;
		new Thread(){
			public void run() {
				for(int i= 0 ;i <= 180;i+=2){
					Message msg = mHandler.obtainMessage();
					msg.what = MSG_TYPE_AUTO_NEXT_PAGE;
					msg.obj = i;
					mHandler.sendMessage(msg);
					try {
						Thread.sleep(5);
					} catch (Exception e) {
					}
				}
				
			};
		}.start();
	}
	
	private static final int MSG_TYPE_AUTO_NEXT_PAGE  = 111;
	private static final int MSG_TYPE_AUTO_BACK_PAGE  = MSG_TYPE_AUTO_NEXT_PAGE + 1;
	
	private Handler mHandler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			int i = (Integer) msg.obj;
			angle = i;
			controller.showFlipAnimation();
			switch (msg.what) {
			case MSG_TYPE_AUTO_NEXT_PAGE:
				if (backCards.getIndex() == -1) {
					angle = 0;
				} else if (backCards.getIndex() == 0) {
					if (angle <= 180 - MAX_TIP_ANGLE)
						angle = 180 - MAX_TIP_ANGLE;
				}
				controller.getSurfaceView().requestRender();
				if(i == 180){
					if (backCards.getIndex() != -1) {
						activeIndex = backCards.getIndex();
						controller.flippedToView(activeIndex);
					}else{
						if (mPageChangelistener != null)
							mPageChangelistener.onPageChange(frontCards.getIndex(), backCards.getIndex());
					}
					controller.postHideFlipAnimation();
					onAutoNext = false;
				}
				break;

			case MSG_TYPE_AUTO_BACK_PAGE:
				controller.getSurfaceView().requestRender();
				if(i == 178){
					if (frontCards.getIndex() >= 1) {
						activeIndex = frontCards.getIndex() -1 ;
						controller.flippedToView(frontCards.getIndex() -1);
					}else{
						swapCards();
						frontCards.setView(-1, null);
						if (mPageChangelistener != null)
							mPageChangelistener.onPageChange(frontCards.getIndex(), backCards.getIndex());
					}
					
				}
				onAutoNext = false;
				break;
			}
			
		};
	};
	
	private void resetAxises() {
		frontCards.getTopCard().setAxis(Card.AXIS_TOP);
		frontCards.getBottomCard().setAxis(Card.AXIS_TOP);
		backCards.getBottomCard().setAxis(Card.AXIS_TOP);
		backCards.getTopCard().setAxis(Card.AXIS_BOTTOM);
	}

	private void swapCards() {
		ViewDualCards tmp = frontCards;
		frontCards = backCards;
		backCards = tmp;
		resetAxises();
	}

	private void applyTexture(FlipRenderer renderer, GL10 gl) {
		frontCards.buildTexture(renderer, gl);
		backCards.buildTexture(renderer, gl);
	}
}